use std::hash;

use axum_app_common::{model::SessionContext, utils::UUID};
use axum_app_dao::{
    entity::{sys_user, SysRoleEntity, SysUserEntity},
    user_dal, UserStatus,
};
use bcrypt::{hash, verify, DEFAULT_COST};
use sea_orm::{
    sea_query::Expr, ActiveModelTrait, ActiveValue::NotSet, ColumnTrait, ConnectionTrait,
    DatabaseBackend, DatabaseConnection, DbBackend, EntityTrait, ExecResult, QueryFilter, Set,
    Statement, Value,
};

use crate::model;
pub struct UserSvc<'a> {
    db_conn: &'a DatabaseConnection,
    ctx: Option<&'a SessionContext>,
}

impl<'a> UserSvc<'a> {
    pub fn new(db_conn: &'a DatabaseConnection, ctx: Option<&'a SessionContext>) -> Self {
        Self { db_conn, ctx }
    }

    /**
     * 创建用户
     */
    pub async fn create(
        &self,
        username: &str,
        display_name: &str,
        mobile: Option<&str>,
        password: Option<&str>,
        status: Option<UserStatus>,
    ) -> anyhow::Result<sys_user::ActiveModel> {
        // 转bcrypt
        let password = password.map(|s| {
            if s.is_empty() {
                return anyhow::Ok(s.to_string());
            }
            Ok(hash(s, DEFAULT_COST)?)
        });

        let password = match password {
            None => "".to_string(),
            Some(core::result::Result::Ok(s)) => s,
            Some(Err(e)) => {
                return Err(anyhow::Error::from(e));
            }
        };

        let status = match status {
            None => UserStatus::Inactive,
            Some(s) => s,
        };
        let status = format!("{:?}", status);

        let user = sys_user::ActiveModel {
            id: NotSet,
            userid: Set(UUID::generate()),
            display_name: Set(display_name.to_string()),
            username: Set(username.to_string()),
            password: Set(password),
            mobile: Set(mobile.map(|s| s.to_string()).unwrap_or(String::default())),
            status: Set(status),
            ..ActiveModelTrait::default()
        };

        self.create_model(user).await
    }

    pub async fn create_model(
        &self,
        user: sys_user::ActiveModel,
    ) -> anyhow::Result<sys_user::ActiveModel> {
        let mut _user = user.clone();
        let id = axum_app_dao::user_dal::insert_user(self.db_conn, user).await?;

        _user.id = Set(id as i64);

        Ok(_user)
    }

    /**
     * 校验密码
     */
    pub async fn find_and_verfy_password(
        &self,
        username: &str,
        password: &str,
    ) -> anyhow::Result<Option<sys_user::Model>> {
        if password.is_empty() || username.is_empty() {
            return Ok(None);
        }

        let user = self.find_by_username(username).await?;

        if user.is_none() {
            return Ok(None);
        }

        let user = user.unwrap();
        let _password = user.password.as_str();
        if _password.is_empty() {
            return Ok(None);
        }

        let valid = self.verify_password(&password, _password)?;
        if !valid {
            return Ok(None);
        }

        Ok(Some(user))
    }

    pub async fn find_by_username(
        &self,
        username: &str,
    ) -> anyhow::Result<Option<sys_user::Model>> {
        let user = user_dal::find_user_by_username(self.db_conn, username).await?;
        Ok(user)
    }

    pub async fn find_by_userid(&self, userid: &str) -> anyhow::Result<Option<sys_user::Model>> {
        let user = user_dal::find_user_by_userid(self.db_conn, userid).await?;
        Ok(user)
    }

    /**
     * 校验密码
     */
    pub fn verify_password(&self, input_passwd: &str, db_passwd: &str) -> anyhow::Result<bool> {
        if input_passwd.is_empty() || db_passwd.is_empty() {
            return Ok(false);
        }

        let valid = verify(input_passwd, db_passwd)?;
        Ok(valid)
    }

    /**
     * 更新用户信息
     */
    pub async fn update_user_by_userid(
        &self,
        userid: &str,
        username: Option<String>,
        display_name: Option<String>,
        mobile: Option<String>,
    ) -> anyhow::Result<usize> {
        if username.is_none() && display_name.is_none() && mobile.is_none() {
            return Ok(0);
        }

        let username_opt = username.map(|s| Box::new(s));
        let display_name_opt = display_name.map(|s| Box::new(s));
        let mobile_opt = mobile.map(|s| Box::new(s));

        let update = sys_user::Entity::update_many()
            .col_expr(
                sys_user::Column::Username,
                Expr::value(Value::String(username_opt)),
            )
            .col_expr(
                sys_user::Column::DisplayName,
                Expr::value(Value::String(display_name_opt)),
            )
            .col_expr(
                sys_user::Column::Mobile,
                Expr::value(Value::String(mobile_opt)),
            )
            .filter(sys_user::Column::Userid.eq(userid))
            .exec(self.db_conn)
            .await?;

        Ok(update.rows_affected as usize)
    }

    /// 软删除用户
    pub async fn soft_delete_by_userid(&self, userid: &str) -> anyhow::Result<usize> {
        let stmt = Statement::from_sql_and_values(
            DbBackend::Postgres,
            r#"update sys_user set delete_flag = id where userid =$1;"#,
            [userid.into()],
        );

        let exec_res: ExecResult = self.db_conn.execute(stmt).await?;
        let rows_affected = exec_res.rows_affected();

        Ok(rows_affected as usize)
    }

    pub async fn toggle_user_status_by_userid(
        &self,
        userid: &str,
        action: &str,
    ) -> anyhow::Result<usize> {
        let status = match action {
            "disable" => UserStatus::Disabled,
            "enable" => UserStatus::Inactive,
            _ => anyhow::bail!("不支持的操作"),
        };

        let status = Some(Box::new(format!("{status:?}")));

        let update = sys_user::Entity::update_many()
            .col_expr(sys_user::Column::Status, Expr::value(Value::String(status)))
            .filter(sys_user::Column::Userid.eq(userid))
            .exec(self.db_conn)
            .await?;

        Ok(update.rows_affected as usize)
    }

    pub async fn update_user_passwd(&self, userid: &str, passwd: &str) -> anyhow::Result<usize> {
        let user_opt = user_dal::find_user_by_userid(self.db_conn, userid).await?;
        if user_opt.is_none() {
            anyhow::bail!("用户不存在");
        }

        // 转bcrypt
        let passwd = hash(passwd, DEFAULT_COST)?;

        let update = sys_user::Entity::update_many()
            .col_expr(
                sys_user::Column::Password,
                Expr::value(Value::String(Some(Box::new(passwd)))),
            )
            .filter(sys_user::Column::Userid.eq(userid))
            .exec(self.db_conn)
            .await?;

        Ok(update.rows_affected as usize)
    }
}

#[cfg(test)]
mod tests {
    use axum_app_dao::UserStatus;
    use sea_orm::DatabaseConnection;

    use crate::service::user_svc::UserSvc;

    pub(crate) async fn init_db() -> anyhow::Result<DatabaseConnection> {
        Ok(axum_app_common::test::get_connect().await)
    }

    #[tokio::test]
    async fn test_create_user() {
        let db = init_db().await.unwrap();
        let res = UserSvc::new(&db, None)
            .create("jaakan22", "孙凯", None, None, Some(UserStatus::Active))
            .await;

        match res {
            Ok(user) => println!("创建成功! ID={:?}", user.id),
            Err(err) => {
                let mut suberr = err.source();
                while let Some(_err) = suberr {
                    println!("err source=======: {:?}", _err);
                    suberr = _err.source();
                }

                let root_err = err.root_cause();

                println!("source: {:?}", err.source());
                println!("root_err: {:?}", root_err);

                if let Some(_err) = root_err.downcast_ref::<sea_orm::RuntimeErr>() {
                    let err_msg = _err.to_string();

                    println!("sql err: {:?}", _err);
                    println!("[err_msg]: {}", err_msg);
                } else {
                    println!("其他异常: {:?}", err);
                }
            }
        }

        // println!("{:?}", res)
    }
}
